package rpc

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
)

type MsHttpClient struct {
	client     http.Client
	serviceMap map[string]MsService
}

func NewHttpClient() *MsHttpClient {
	// Transport 请求分发， 携程池安全， 支持连接池
	client := http.Client{
		Timeout: time.Duration(3) * time.Second,
		Transport: &http.Transport{
			MaxIdleConnsPerHost:   5,
			MaxConnsPerHost:       100,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}
	return &MsHttpClient{
		client:     client,
		serviceMap: make(map[string]MsService),
	}
}

func (c *MsHttpClient) GetRequest(method, url string, args map[string]any) (*http.Request, error) {
	if args != nil && len(args) > 0 {
		url = url + "?" + c.toValues(args)
	}
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		return nil, err
	}
	return req, nil
}

func (c *MsHttpClient) FormRequest(method, url string, args map[string]any) (*http.Request, error) {
	request, err := http.NewRequest(method, url, strings.NewReader(c.toValues(args)))
	if err != nil {
		return nil, err
	}
	return request, nil
}

func (c *MsHttpClient) JsonRequest(method, url string, args map[string]any) (*http.Request, error) {
	var (
		jsonByte []byte
		err      error
		request  *http.Request
	)
	if args == nil {
		args = map[string]any{}
	}
	if jsonByte, err = json.Marshal(args); err != nil {
		return nil, err
	}

	if request, err = http.NewRequest(method, url, bytes.NewBuffer(jsonByte)); err != nil {
		return nil, err
	}

	return request, nil
}

func (c *MsHttpClientSession) Response(req *http.Request) ([]byte, error) {
	return c.handleResponse(req)
}

func (c *MsHttpClientSession) DoResponse(req *http.Request) ([]byte, error) {
	return c.handleResponse(req)
}

func (c *MsHttpClientSession) Get(url string, args map[string]any) ([]byte, error) {
	if args != nil && len(args) > 0 {
		url = url + "?" + c.toValues(args)
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	return c.handleResponse(req)
}

func (c *MsHttpClientSession) PostForm(url string, args map[string]any) ([]byte, error) {
	request, err := http.NewRequest("POST", url, strings.NewReader(c.toValues(args)))
	if err != nil {
		return nil, err
	}
	return c.handleResponse(request)
}

func (c *MsHttpClientSession) PostJson(url string, args map[string]any) ([]byte, error) {
	var (
		jsonByte []byte
		err      error
		request  *http.Request
	)
	if args == nil {
		args = map[string]any{}
	}
	if jsonByte, err = json.Marshal(args); err != nil {
		return nil, err
	}

	if request, err = http.NewRequest("POST", url, bytes.NewBuffer(jsonByte)); err != nil {
		return nil, err
	}
	return c.handleResponse(request)
}

func (c *MsHttpClientSession) handleResponse(req *http.Request) ([]byte, error) {

	// 链路追踪调用 调用外部设置的请求
	c.ReqHandler(req)

	var err error
	response, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	if response.StatusCode != 200 {
		return nil, errors.New(response.Status)
	}
	buffLen := 79
	reader := bufio.NewReader(response.Body)
	buff := make([]byte, buffLen)
	body := make([]byte, 0)
	var n int
	for {
		n, err = reader.Read(buff)
		if err == io.EOF || n == 0 {
			break
		}
		body = append(body, buff[:n]...)
		if n < buffLen { // 决定是否进行下一次循环
			break
		}
	}
	defer response.Body.Close()
	if err != nil {
		return nil, err
	}
	return body, nil
}

func (c *MsHttpClient) toValues(args map[string]any) string {
	if args != nil && len(args) > 0 {
		params := url.Values{}
		for k, v := range args {
			params.Set(k, fmt.Sprintf("%v", v))
		}
		return params.Encode() // url.Values 这种数据类型编码后会将参数，按照name=xx&age=xx返回
	}
	return ""
}

// ====================================================================================

type MsHttpClientSession struct {
	*MsHttpClient
	ReqHandler func(req *http.Request)
}

func (c *MsHttpClient) NewSession() *MsHttpClientSession {
	return &MsHttpClientSession{c, nil}
}

type MsService interface {
	Env() Config
}

func (c *MsHttpClient) Use(name string, s MsService) {
	if c.serviceMap == nil {
		c.serviceMap = make(map[string]MsService)
	}
	c.serviceMap[name] = s
}

func (c *MsHttpClient) Session() *MsHttpClientSession {
	session := MsHttpClientSession{}
	session.MsHttpClient = c
	return &session
}

func (c *MsHttpClientSession) Do(name string, method string) MsService {
	s, ok := c.serviceMap[name]
	if !ok {
		panic(errors.New("name" + "not exist,please action"))
	}
	t := reflect.TypeOf(s)
	v := reflect.ValueOf(s)
	if t.Kind() != reflect.Pointer {
		panic(errors.New("service must be pointer"))
	}
	tVar := t.Elem()
	vVar := v.Elem()
	findIndex := -1
	for i := 0; i < tVar.NumField(); i++ {
		field := tVar.Field(i)
		mName := field.Name
		if method == mName {
			findIndex = i
			break
		}
	}
	if findIndex == -1 {
		panic(errors.New(method + "not exist"))
	}
	requestPath := tVar.Field(findIndex).Tag.Get("msrpc")
	if requestPath == "" {
		panic(errors.New("msrpc tag not exist"))
	}
	split := strings.Split(requestPath, ",")
	mt := split[0]
	path := split[1]
	config := s.Env()
	prefix := config.Url()
	// 构造方法的实现，外部结构体只对内部的方法属性进行了声明
	f := func(args map[string]any) ([]byte, error) {
		if mt == GET {
			return c.Get(prefix+path, args)
		}
		if mt == POSTForm {
			return c.PostForm(prefix+path, args)
		}
		if mt == POSTForm {
			return c.PostJson(prefix+path, args)
		}
		return nil, nil
	}
	value := reflect.ValueOf(f)
	vVar.Field(findIndex).Set(value)
	return s
}
